package bytebuddys.advice;

import bytebuddys.advice.target.Cooker;
import bytebuddys.advice.target.Cooker2;
import com.sun.tools.javac.comp.Annotate;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ByteArrayClassLoader;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.instrument.ClassFileTransformer;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TestAdvice {
    public static TestAdvice INSTANCE = new TestAdvice();

    ClassLoader classLoader;


    /**
     * 加载未被修改的类
     */
    public void initClassLoader() throws Exception {

        classLoader = new ByteArrayClassLoader.ChildFirst(getClass().getClassLoader(),
                ClassFileLocator.ForClassLoader.readToNames(Cooker.class),
                ByteArrayClassLoader.PersistenceHandler.MANIFEST);


    }


    /**
     * modify
     */
    public void modifyTarget() throws Exception {
        ByteBuddyAgent.install();


        ElementMatcher a = ElementMatchers.any();
        System.out.println(a.toString());
        ElementMatcher b =ElementMatchers.not(ElementMatchers.isStatic()).and(ElementMatchers.any());
        System.out.println(b.toString());

        ClassFileTransformer classFileTransformer = new AgentBuilder.Default()
                .with(AgentBuilder.PoolStrategy.Default.EXTENDED)
                .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
                //.ignore()
                // 设定匹配范围
                .type(ElementMatchers.is(Cooker.class), ElementMatchers.is(classLoader)).transform(new AgentBuilder.Transformer() {
                    @Override
                    public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {
                        // 对比 enter 设置为 @Advice.This(optional = false)
                        //      exit 设置为 @Advice.This(optional = true )

                        return builder.visit(
                                Advice.withCustomMapping()
                                        .bind(TestAnnotation.class, "demo")
                                        .to(AdviceLogic.class)
                                        .on(ElementMatchers.not(ElementMatchers.isStatic()).and(ElementMatchers.any())));
//                       return builder.visit(Advice.to(AdviceLogic.class).on(ElementMatchers.isMethod().and(ElementMatchers.not(ElementMatchers.isStatic()))));

                    }
                })
                .installOnByteBuddyAgent();


    }


    public void defineNewClass() throws Exception {
        Class<?> cookerType = new ByteBuddy()
                .redefine(Cooker.class)
                .visit(Advice.withCustomMapping()
                        .bind(TestAnnotation.class, "demo")
                        .to(AdviceLogic.class).on(ElementMatchers.not(ElementMatchers.isConstructor())))
                .make()
                .load(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassLoadingStrategy.Default.WRAPPER)
                .getLoaded();
        new ByteBuddy()
                .redefine(Cooker.class)
                .visit(Advice.withCustomMapping()
                        .bind(TestAnnotation.class, "demo")
                        .to(AdviceLogic.class).on(ElementMatchers.not(ElementMatchers.isConstructor())))
                .make()
                .saveIn(new File("./ttt.class"));

        Object cooker = cookerType.getDeclaredConstructor().newInstance();
        // 查看注解

        for (Field f : cookerType.getFields()) {

            Object[] a = f.getDeclaredAnnotations();
            System.out.println();
        }

        // 所有方法的调用
        cookerType.getDeclaredMethod("hello").invoke(cooker);


        cookerType.getDeclaredMethod("taste", String.class).invoke(null, "pototo");
        cookerType.getMethod("makeFood", String.class, int.class, Double[].class).invoke(cooker, "pototo", 1, new Double[]{1.0, 2.0});

        cookerType.getDeclaredMethod("getInstance").invoke(cooker);


    }

    public void print() throws Exception {

        Class<Cooker> cookerType = (Class<Cooker>) classLoader.loadClass(Cooker.class.getName());
        Object Cooker = cookerType.getDeclaredConstructor().newInstance();
        cookerType.getDeclaredMethod("hello").invoke(Cooker);
        cookerType.getDeclaredMethod("taste", String.class).invoke(null, "pototo");
        cookerType.getMethod("makeFood", String.class, int.class, Double[].class).invoke(Cooker, "pototo", 1, new Double[]{1.0, 2.0});
//        cookerType.getMethod("makeFood", String.class, int.class, Double[].class).invoke(Cooker, "pototo", 1, null) ;
        Class<Cooker2> cookerType2 = (Class<Cooker2>) classLoader.loadClass(Cooker2.class.getName());
        Object Cooker2 = cookerType2.getDeclaredConstructor().newInstance();
        cookerType2.getDeclaredMethod("hello").invoke(Cooker2);
        cookerType2.getDeclaredMethod("taste", String.class).invoke(null, "pototo");
        cookerType2.getMethod("makeFood", String.class, int.class, Double[].class).invoke(Cooker2, "pototo", 1, new Double[]{1.0, 2.0});

    }

    public static void main(String[] args) throws Exception {

        String info = "{" +
                "\"enhanceName\": \"" + "a" +

                ",classMatch: " + "b" +
                ",methodMatch: " + "methodMatch" +
                ",witenessMatch" + "witenessMatch" +
                "}";
        System.out.println(info);


        INSTANCE.initClassLoader();

        INSTANCE.modifyTarget();
//        INSTANCE.print();
        INSTANCE.defineNewClass();


    }
}
